﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
using System.Windows.Forms.DataVisualization.Charting;
using HeThongDoDien.IO;
using C1.Win.C1FlexGrid;
using System.IO;
using Settings = HeThongDoDien.Properties.Settings;

namespace HeThongDoDien
{
    public partial class frmMain : Form
    {
        public frmMain()
        {
            InitializeComponent();
        }

        #region Local Fields

        private RSC232Connector m_force_connector;
        private RSC232Connector m_distance_connector;

        private DataTable m_dt_data;
        private Queue<double> m_force_buffer;
        private Queue<double> m_distance_buffer;
        #endregion

        #region Graph Definition

        private void InitializeGraph()
        {
            this.chart1.ChartAreas[0].AxisX.Title = "Distance (mm)";
            this.chart1.ChartAreas[0].AxisY.Title = "Force (N)";
            this.chart1.ChartAreas[0].AxisX.TitleAlignment = StringAlignment.Center;
            this.chart1.ChartAreas[0].AxisY.TitleAlignment = StringAlignment.Center;
            chart1.Titles[0].Text = "Correlation Graph";
            //this.chart1.ChartAreas[0].AxisX.TitleFont = new Font(this.chart1.ChartAreas[0].AxisX.TitleFont, FontStyle.Bold);
            //this.chart1.ChartAreas[0].AxisY.TitleFont = new Font(this.chart1.ChartAreas[0].AxisX.TitleFont, FontStyle.Bold);
        }

        private void DrawGraph(DataTable data)
        {
            this.chart1.Series[0].XValueMember = "Distance";
            this.chart1.Series[0].YValueMembers = "Force";
            this.chart1.DataSource = data;
            this.chart1.Invalidate();
        }


        private DataTable GenerateTestData()
        {
            var dt = new DataTable();
            dt.Columns.Add("Time").Caption = "Time (ms)";
            dt.Columns.Add("Force").Caption = "Force (N)";
            dt.Columns.Add("Distance").Caption = "Distance (mm)";
            var plotX = 0.001;   // Đơn vị nhỏ nhất của quãng đường
            var plotY = 0.01;   // Đơn vị nhỏ nhất của lực
            var random = new Random();
            for (int pointIndex = 1; pointIndex < 1500; pointIndex++)
            {
                plotX += random.NextDouble() * 10.0 - 5.0;
                plotY += random.NextDouble() * 10.0 - 5.0;
                var row = dt.NewRow();
                row["Time"] = pointIndex;
                row["Force"] = plotY;
                row["Distance"] = plotX;
                dt.Rows.Add(row);
            }
            return dt;
        }

        private void SaveGraphImage()
        {
            var savedialog = new SaveFileDialog();
            savedialog.InitialDirectory = Application.StartupPath;
            savedialog.Filter = "Portable Network Graphics (*.Png)|*.png|Joint Photographic Experts Group (*.Jpeg)|*.jpeg|Graphics Interchange Format (*.Gif)|*.gif|BMP Format (*.bmp)|*.bmp";
            var dr = savedialog.ShowDialog();
            if (dr == DialogResult.OK)
            {
                var extension = Path.GetExtension(savedialog.FileName);
                switch (extension)
                {
                    case ".png": this.chart1.SaveImage(savedialog.FileName, ChartImageFormat.Png);
                        break;
                    case ".jpeg": this.chart1.SaveImage(savedialog.FileName, ChartImageFormat.Jpeg);
                        break;
                    case ".gif": this.chart1.SaveImage(savedialog.FileName, ChartImageFormat.Gif);
                        break;
                    case ".bmp": this.chart1.SaveImage(savedialog.FileName, ChartImageFormat.Bmp);
                        break;
                    default:
                        throw new Exception("Image format not support");
                }
            }
        }

        #endregion

        #region C1 Definition

        private void c1FlexGrid_OwnerDrawCell(object sender, OwnerDrawCellEventArgs e)
        {
            var c1fg = (C1FlexGrid)sender;
            if ((e.Row >= c1fg.Rows.Fixed) & (e.Col == (c1fg.Cols.Fixed - 1)))
            {
                e.Text = ((e.Row - c1fg.Rows.Fixed) + 1).ToString();
            }
        }


        //Ham xuat Excel
        private void SaveDataExcel()
        {
            var savedialog = new SaveFileDialog();
            savedialog.InitialDirectory = Application.StartupPath;
            savedialog.Filter = "Excel format (*.xls)|*.xls";
            var dr = savedialog.ShowDialog();
            if (dr == DialogResult.OK)
            {
                this.c1FlexGrid1.SaveExcel(savedialog.FileName, string.Empty, FileFlags.IncludeFixedCells);

            }
        }

        #endregion

        private void TestConnection()
        {
            var portNames = SerialPort.GetPortNames();
            var forcePortName = Settings.Default.ForcePortName;
            var distancePortName = Settings.Default.DistancePortName;
            if (portNames.Contains(forcePortName) == true && portNames.Contains(distancePortName) == true)
            {
                this.btnConfiguration.Image = Properties.Resources.plug_disconnect;
                this.tsLabelPortNames.Text = string.Format("{0} - {1}", forcePortName, distancePortName);
            }
            else
            {
                this.btnConfiguration.Image = Properties.Resources.plug_disconnect_slash;
                this.tsLabelPortNames.Text = "? - ?";
            }
        }

        private void InitializeField()
        {
            this.m_dt_data = new DataTable();
            this.m_force_buffer = new Queue<double>();
            this.m_distance_buffer = new Queue<double>();
            this.m_force_connector = new RSC232Connector();
            this.m_distance_connector = new RSC232Connector();
            this.m_force_connector.DataIncoming += new DataIncomingHandler(m_force_connector_DataIncoming);
            this.m_distance_connector.DataIncoming += new DataIncomingHandler(m_distance_connector_DataIncoming);
            // Configurate
            this.m_dt_data.Columns.Add("Time", typeof(int));
            this.m_dt_data.Columns.Add("Force", typeof(double));
            this.m_dt_data.Columns.Add("Distance", typeof(double));
            this.c1FlexGrid1.DataSource = this.m_dt_data;
            this.c1FlexGrid1.ScrollBars = ScrollBars.Vertical;
            this.c1FlexGrid1.Cols["Time"].WidthDisplay = 75;
            this.c1FlexGrid1.Cols["Time"].TextAlign = TextAlignEnum.CenterCenter;
            this.c1FlexGrid1.Cols["Time"].Caption = "Time (ms)";
            this.c1FlexGrid1.Cols["Force"].Caption = "Force (N)";
            this.c1FlexGrid1.Cols["Distance"].Caption = "Distance (mm)";
            this.c1FlexGrid1.Cols["Force"].WidthDisplay = 125;
            this.c1FlexGrid1.Cols["Distance"].WidthDisplay = 125;
        }

        private void SynchronizeSerialData(double force, double distance)
        {
            var dlen = this.m_dt_data.Rows.Count + 1;
            var row = this.m_dt_data.NewRow();
            row["Time"] = dlen;
            row["Force"] = force;
            row["Distance"] = distance;
            this.m_dt_data.Rows.Add(row);
            this.Invoke(new MethodInvoker(delegate()
            {
                this.c1FlexGrid1.TopRow = dlen;
                this.c1FlexGrid1.Redraw = true;
                this.tsLabelData.Text = string.Format("Count: {0}", dlen);
            }));
        }

        #region Test Fields && Read Data
        private StreamWriter m_force_stream;
        private StreamWriter m_distance_stream;
        #endregion

        private void frmMain_Load(object sender, EventArgs e)
        {
            try
            {
                this.c1FlexGrid1.DrawMode = DrawModeEnum.OwnerDraw;
                this.c1FlexGrid1.OwnerDrawCell += new OwnerDrawCellEventHandler(c1FlexGrid_OwnerDrawCell);
                // Initialize fields
                this.InitializeField();
                // Initialize chart
                this.InitializeGraph();
                // Check connection
                this.TestConnection();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }


        private void btnStop_Click(object sender, EventArgs e)
        {
            try
            {
                try
                {
                    this.m_force_connector.Close();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
                try
                {
                    this.m_distance_connector.Close();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
                // Finally
                this.btnStop.Enabled = false;
                this.btnStart.Enabled = true;
                this.btnConfiguration.Enabled = true;
                this.stopToolStripMenuItem.Enabled = false;
                this.startToolStripMenuItem.Enabled = true;
                this.configurationToolStripMenuItem.Enabled = true;
                this.btnConfiguration.Image = Properties.Resources.plug_disconnect;
                // Data
                this.tsLabelData.Text = string.Format("Count: {0}", m_dt_data.Rows.Count);
                // Test
                // Close stream
                if (this.m_force_stream != null)
                    this.m_force_stream.Close();
                if (this.m_distance_stream != null)
                    this.m_distance_stream.Close();
                // If buffer still fully
                while (this.m_distance_buffer.Count > 0 && this.m_force_buffer.Count > 0)
                {
                    var f = this.m_force_buffer.Dequeue();
                    var d = this.m_distance_buffer.Dequeue();
                    this.SynchronizeSerialData(f, d);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        // Vunb
        private void btnStart_Click(object sender, EventArgs e)
        {
            try
            {
                #region Test Data Region
                //var data = GenerateTestData();
                //// Draw
                //this.InitializeGraph();
                //this.DrawGraph(data);
                //// Display
                //this.c1FlexGrid1.Cols[0].Name = "Time";
                //this.c1FlexGrid1.Cols[1].Name = "Force";
                //this.c1FlexGrid1.Cols[2].Name = "Distance";
                //this.c1FlexGrid1.DataSource = data;
                //this.c1FlexGrid1.Cols["Time"].WidthDisplay = 50;
                //this.c1FlexGrid1.Cols["Time"].TextAlign = TextAlignEnum.CenterCenter;
                //this.c1FlexGrid1.Cols["Time"].Caption = "Time (ms)";
                //this.c1FlexGrid1.Cols["Force"].Caption = "Force (N)";
                //this.c1FlexGrid1.Cols["Distance"].Caption = "Distance (mm)";
                #endregion
                // Format control
                this.tsLabelData.Text = "Count: 0";
                // Initialize
                this.m_dt_data.Rows.Clear();
                this.m_force_buffer.Clear();
                this.m_distance_buffer.Clear();
                // Get Stream Data Sample
                this.m_force_stream = new StreamWriter(File.Open("Force.txt", FileMode.Create, FileAccess.Write));
                this.m_distance_stream = new StreamWriter(File.Open("Distance.txt", FileMode.Create, FileAccess.Write));
                // PortName
                var forcePortName = Settings.Default.ForcePortName;
                var distancePortName = Settings.Default.DistancePortName;
                // BaudRate
                var forceBaudRate = Settings.Default.ForceBaudRate;
                var distanceBaudRate = Settings.Default.DistanceBaudRate;
                // Configurate
                this.m_force_connector.PortName = forcePortName;
                this.m_force_connector.BaudRate = forceBaudRate;
                this.m_distance_connector.PortName = distancePortName;
                this.m_distance_connector.BaudRate = distanceBaudRate;
                // Open Connection
                this.m_force_connector.Open();
                this.m_distance_connector.Open();

                // Signal
                this.btnStart.Enabled = false;
                this.btnStop.Enabled = true;
                this.btnConfiguration.Image = Properties.Resources.plug_connect;
                this.btnConfiguration.Enabled = false;
                // Menu
                this.stopToolStripMenuItem.Enabled = true;
                this.startToolStripMenuItem.Enabled = false;
                this.configurationToolStripMenuItem.Enabled = false;
            }
            catch (Exception ex)
            {
                this.m_force_connector.Close();
                this.m_distance_connector.Close();
                this.btnStop.Enabled = false;
                this.btnStart.Enabled = true;
                this.btnConfiguration.Enabled = true;
                this.stopToolStripMenuItem.Enabled = false;
                this.startToolStripMenuItem.Enabled = true;
                // Close stream
                if (this.m_force_stream != null)
                    this.m_force_stream.Close();
                if (this.m_distance_stream != null)
                    this.m_distance_stream.Close();
                MessageBox.Show(ex.Message);
            }
        }
        /// <summary>
        /// Giải mã dữ liệu + đồng bộ
        /// </summary>
        private void m_distance_connector_DataIncoming(object sender, byte[] data)
        {
            lock (this.m_distance_buffer)
            {
                try
                {
                    // Test data
                    if (data != null && data.Length > 0)
                    {
                        var base64 = Convert.ToBase64String(data);
                        this.m_distance_stream.WriteLine(string.Format("Length({0}): {1}", data.Length, base64));
                        this.m_distance_stream.Flush();
                    }
                    var value = BitConverter.ToDouble(data, 0);
                    this.m_distance_buffer.Enqueue(value);

                    if (this.m_distance_buffer.Count > 0 && this.m_force_buffer.Count > 0)
                    {
                        var f = this.m_force_buffer.Dequeue();
                        var d = this.m_distance_buffer.Dequeue();
                        this.SynchronizeSerialData(f, d);
                    }
                }
                catch (Exception ex)
                {
                    var error = File.AppendText("error.txt");
                    error.WriteLine("Message: " + ex.Message);
                    error.WriteLine("Stack Trace: " + ex.StackTrace);
                    error.WriteLine("Exception: " + ex.ToString());
                    //MessageBox.Show("Error while receive data: " + ex.Message);
                }
            }
        }
        /// <summary>
        /// Giải mã dữ liệu + đồng bộ
        /// </summary>
        private void m_force_connector_DataIncoming(object sender, byte[] data)
        {
            lock (this.m_force_buffer)
            {
                try
                {
                    // Test data
                    if (data != null && data.Length > 0)
                    {
                        var base64 = Convert.ToBase64String(data);
                        this.m_force_stream.WriteLine(string.Format("Length({0}): {1}", data.Length, base64));
                        this.m_force_stream.Flush();
                    }
                    var value = BitConverter.ToDouble(data, 0);
                    this.m_force_buffer.Enqueue(value);
                }
                catch (Exception ex)
                {
                    var error = File.AppendText("error.txt");
                    error.WriteLine("Message: " + ex.Message);
                    error.WriteLine("Stack Trace: " + ex.StackTrace);
                    error.WriteLine("Exception: " + ex.ToString());
                    //MessageBox.Show("Error while receive data: " + ex.Message);
                }

            }
        }

        private void btnSave_Click(object sender, EventArgs e)
        {
            try
            {
                if (this.tabControl1.SelectedTab.Name == this.tabDataPage.Name)
                {
                    this.SaveDataExcel();
                }
                else if (this.tabControl1.SelectedTab.Name == this.tabVisualisationPage.Name)
                {
                    this.SaveGraphImage();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void btnHelp_Click(object sender, EventArgs e)
        {
            // Trinhnv
        }

        private void captureGraphToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                this.SaveGraphImage();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void saveDataToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                this.SaveDataExcel();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                this.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);

            }
        }

        private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {
                if (this.m_dt_data != null && tabControl1.SelectedTab.Name == tabVisualisationPage.Name)
                {
                    this.DrawGraph(this.m_dt_data);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void btnConfiguration_Click(object sender, EventArgs e)
        {
            try
            {
                var configurator = new frmConfiguration();
                var dr = configurator.ShowDialog();
                if (dr == DialogResult.OK)
                {
                    // Get Update
                    var forcePortName = Settings.Default.ForcePortName;
                    var distancePortName = Settings.Default.DistancePortName;
                    this.btnConfiguration.Image = Properties.Resources.plug_disconnect;
                    this.tsLabelPortNames.Text = string.Format("{0} - {1}", forcePortName, distancePortName);
                }

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        #region Backup Test Code
        //System.Data.DataSet ds = new System.Data.DataSet();
        //ds.ReadXml("Test.xml");
        //chart1.DataSource = ds;
        //chart1.Series[0].XValueMember = ds.Tables[0].Columns[0].ToString();
        //chart1.Series[0].YValueMembers = ds.Tables[0].Columns[1].ToString();
        //chart1.Series.Add("Series2");
        //chart1.ChartAreas["ChartArea1"].AxisX.Interval = 1;

        //chart1.Series["Series2"].XValueMember = ds.Tables[0].Columns[0].ToString();
        //chart1.Series["Series2"].YValueMembers = ds.Tables[0].Columns[2].ToString();
        //chart1.Titles.Add("Title1");
        //chart1.Titles["Title1"].Text = "Temperature Month Table";
        //chart1.ChartAreas["ChartArea1"].AxisX.Title = "City";
        //chart1.ChartAreas["ChartArea1"].AxisY.Title = "Temperature";

        //FillData();
        //chart1.Series[0].ChartType = SeriesChartType.Line;

        //private void FillData()
        //{
        //    double plotX = 50.0;
        //    double plotY = 100.0;
        //    if (chart1.Series[0].Points.Count > 0)
        //    {
        //        plotX = chart1.Series[0].Points[chart1.Series[0].Points.Count - 1].YValues[0];
        //    }
        //    Random random = new Random();
        //    for (int pointIndex = 0; pointIndex < 20000; pointIndex++)
        //    {
        //        plotX = plotX + (float)(random.NextDouble() * 10.0 - 5.0);
        //        plotY = plotY + (float)(random.NextDouble() * 10.0 - 5.0);
        //        chart1.Series[0].Points.AddXY(plotX, plotY);
        //    }

        //}
        #endregion


    }
}
